package com.macro.mall.common.exception;

import cn.dev33.satoken.exception.NotPermissionException;
import cn.hutool.core.util.StrUtil;
import com.macro.mall.common.api.CommonResult;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;

import java.sql.SQLSyntaxErrorException;
import java.util.List;

/**
 * 全局异常处理
 * Created by macro on 2020/2/27.
 */
@ControllerAdvice
public class GlobalExceptionHandler {

    @ResponseBody
    @ExceptionHandler(value = ApiException.class)
    public CommonResult handle(ApiException e) {
        if (e.getErrorCode() != null) {
            return CommonResult.failed(e.getErrorCode());
        }
        return CommonResult.failed(e.getMessage());
    }
    /**
     * 空指针异常
     */
    @ResponseBody
    @ExceptionHandler(value = NullPointerException.class)
    public CommonResult handleValidException(NullPointerException e) {

        return CommonResult.nullPointException(e.getMessage());
    }

    /**
     *
     * 当请求体中的数据未通过验证注解
     * （如 @NotNull, @Size, @Min, @Max 等）的检查时
     * ，会抛出此异常
     * 字段校验不通过
     */
    @ResponseBody
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public CommonResult handleValidException(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        String message = null;
        if (bindingResult.hasErrors()) {
            FieldError fieldError = bindingResult.getFieldError();
            if (fieldError != null) {
                // message = fieldError.getField()+fieldError.getDefaultMessage();
                message = fieldError.getDefaultMessage();

            }
        }
        return CommonResult.validateFailed(message);
    }
    /**
     * 字段校验不通过
     */
    @ResponseBody
    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    public CommonResult handleValidException() {
        return CommonResult.validateFailed("参数缺失");
    }


    /**
     * 1.无效的参数值： 方法接收到一个值，但该值超出了方法的有效范围或不符合方法的预期。
     * 2.空参数： 有些方法不允许参数为 null，而传递了 null 参数。
     * 3.参数类型不匹配： 期望的参数类型与实际传递的参数类型不匹配。
     */
    @ResponseBody
    @ExceptionHandler(value = IllegalArgumentException.class)
    public CommonResult handleValidException(IllegalArgumentException e) {
        return CommonResult.validateFailed("参数异常");
    }

    /**
     *
     * 并将带有Bean Validation注解的对象作为方法参数时，如果参数对象使用了来自 javax.validation 包下的注解（如@NotNull、@Size、@Pattern等）进行数据校验，在执行业务逻辑或持久化操作之前，Hibernate Validator（作为Java Bean Validation规范的实现库）会自动进行验证；如果对象的某个属性值不满足这些注解所定义的约束条件，会抛出javax.validation.ConstraintViolationException异常。
     * 需要满足如下3个条件：
     *
     * 1.Controller上添加 @Validated 注解；
     * 2.需要校验的接口入参， 直接 映射到控制器方法参数；
     * 3.参数前添加了校验规则注解（比如 @Pattern）。
     *
     * 字段校验不通过(controller)
     */
    @ResponseBody
    @ExceptionHandler(value = ConstraintViolationException.class)
    public CommonResult handleValidException(ConstraintViolationException e) {
        String message = null;
        List<String> collect = e.getConstraintViolations()
                .stream()
                .map(violation ->  violation.getMessage())
                .toList();
        if (!collect.isEmpty()) {
            message = String.join(",", collect);
        }
        return CommonResult.validateFailed(message);
    }
    /**
     * 接收客户端发送的数据时，
     * 数据的格式不正确或
     * 者无法被解析为相应的对象或
     * 为null
     */
    @ResponseBody
    @ExceptionHandler(value = HttpMessageNotReadableException.class)
    public CommonResult handleValidException(HttpMessageNotReadableException e) {

        return CommonResult.validateFailed();
    }

    /**
     * 1。参数映射错误：请求中的参数未能正确地映射到目标对象的属性上。
     * 2.注解校验失败：对象属性使用了javax.validation或者org.springframework.validation包下的注解进行校验（如@NotNull、@Size等），而传入的值不符合这些注解所定义的约束条件。
     *
     *1.必填字段为空（或空白）：若某个字符串类型的字段标记为@NotBlank，但未接收到对应的参数或参数为空白（不包含非空格的字符），则会触发BindException异常。@NotNull、@NotEmpty 的逻辑类似，校验失败也会触发BindException异常。
     * 2.数据格式不匹配：例如，尝试将请求参数的字符串值转换为目标类型（如整数或日期）时无法成功转换。
     * 3.字段长度超出限制：对于有最大或最小长度限制的字段，当请求参数的长度超过这些限制时，会引发异常。
     * 4.正则表达式不匹配：当字段的值需满足特定的正则表达式模式，而实际提供的参数不满足该模式时，也会导致异常。
     * 5.自定义验证逻辑失败：通过@Valid注解配合自定义验证器进行参数校验时，如果验证失败，同样会抛出BindException异常。
     */
    @ResponseBody
    @ExceptionHandler(value = BindException.class)
    public CommonResult handleValidException(BindException e) {
        BindingResult bindingResult = e.getBindingResult();
        String message = null;
        if (bindingResult.hasErrors()) {
            FieldError fieldError = bindingResult.getFieldError();
            if (fieldError != null) {
                message = fieldError.getField()+fieldError.getDefaultMessage();
            }
        }
        return CommonResult.validateFailed(message);
    }


    /**
     * 数据库异常
     */
    @ResponseBody
    @ExceptionHandler(value = SQLSyntaxErrorException.class)
    public CommonResult handleSQLSyntaxErrorException(SQLSyntaxErrorException e) {
        String message = e.getMessage();
        if (StrUtil.isNotEmpty(message) && message.contains("denied")) {
            message = "演示环境暂无修改权限，如需修改数据可本地搭建后台服务！";
        }
        return CommonResult.failed(message);
    }
    /**
     * 鉴权异常
     */
    @ResponseBody
    @ExceptionHandler(value = NotPermissionException.class)
    public CommonResult handleNotPermissionException( ) {
        return CommonResult.forbidden(null);

    }




  /*    @ExceptionHandler(ArithmeticException.class)//ArithmeticException异常类型通过注解拿到
    public String exceptionHandler(HttpServletRequest request, ArithmeticException exception){
        // 打印详细信息
        log(request,exception);
        return exception.getMessage();

    }


    public void log(HttpServletRequest request, Exception exception) {
        //换行符
        String lineSeparatorStr = System.getProperty("line.separator");

        StringBuilder exStr = new StringBuilder();
        StackTraceElement[] trace = exception.getStackTrace();
        // 获取堆栈信息并输出为打印的形式
        for (StackTraceElement s : trace) {
            exStr.append("\tat " + s + "\r\n");
        }
       *//* //打印error级别的堆栈日志
        log.error("访问地址：" + request.getRequestURL() + ",请求方法：" + request.getMethod() +
                ",远程地址：" + request.getRemoteAddr() + lineSeparatorStr +
                "错误堆栈信息如下:" + exception.toString() + lineSeparatorStr + exStr);
 *//*   }
     */
}
